#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <math.h>
#include <time.h>
#include <sys.h>
#include <string.h>

#include "map.h"
#include "msx2lib.h"

uchar StartX,StartY,EndX,EndY;
uchar Start_End_dist;

void calc_distance(uint start);
extern uchar map[mapHeight][mapWidth];
extern uchar cmap[mapHeight][mapWidth];

void popolate_level(void);
void remap_level(void);

// probe function

void show(uchar map[][mapWidth]) {
	int i,j;
	int v = 0x1800;
	DI();
	for (j=0;j<mapHeight;j++) {
		outp(0x99,(v) & 255);
		outp(0x99,((v)>>8) | 0x40);
		for (i=0;i<mapWidth;i++) 
			outp(0x98,map[j][i]);
		v+=32;
	}
	EI();
}

// wip

void	set(char x,char y,char v);


void popolate_level(void) {
	uchar i,j,k;
	uchar t;
	uchar kX,kY,kDist;
	uchar keyNum;
	uchar doorNum;
		
	for (j=0;j<mapHeight;j++) {
		for (i=0;i<mapWidth;i++) 
			switch (worldMap[j][i]) {
			case wll : 	map[j][i] = 0; break;
			case flr : 	
			default: 	map[j][i] = 255; break;
			}
	}
	
	// put in cmap the metrics computed from starting point 
	calc_distance(StartX*256+StartY);


	set(StartX,StartY,1);
	
	// chose as end point the FARTHEST dead end from start point
			
	Start_End_dist = 0;
	
	for (j=1;j<mapHeight;j+=2) 
		for (i=1;i<mapWidth;i+=2) 
		{
			if ((cmap[j][i]> Start_End_dist) && (map[j][i]==255) && (((cmap[j+1][i]==0)+(cmap[j-1][i]==0)+(cmap[j][i+1]==0)+(cmap[j][i-1]==0)) == 3) ) {
					EndX=i;
					EndY=j; 
					Start_End_dist = cmap[j][i];
				}
		}

	set(EndX,EndY,2);
		
	// current distance start - next key
	
	kDist = t = Start_End_dist*4/5;	
	
	// set a the door Z to close the exit
	
	doorNum = 'Z'+1;
	keyNum = 'z'+1;
	
	// set as exit the farthest key
	
	kX = EndX;	
	kY = EndY;
	
	do
	{
		doorNum--;
		keyNum--;

		// set the next door close to the key

		if (map[kY+1][kX]==255)	worldMap[kY+1][kX] = doorNum;
		else 
		if (map[kY-1][kX]==255)	worldMap[kY-1][kX] = doorNum;
		else 
		if (map[kY][kX+1]==255)	worldMap[kY][kX+1] = doorNum;
		else
		if (map[kY][kX-1]==255)	worldMap[kY][kX-1] = doorNum;

		
		// set the next key in a dead end closer to the start

		if (kDist<t) 
			t = kDist;
		t--;
		
		kDist = 0;

		for (k=t;k>2;k--)
		{
			for (j=1;j<mapHeight;j+=2) 
				for (i=1;i<mapWidth;i+=2) 
				{
					if ((cmap[j][i]==k) && (map[j][i]==255) && (((cmap[j+1][i]==0)+(cmap[j-1][i]==0)+(cmap[j][i+1]==0)+(cmap[j][i-1]==0)) == 3) ) {
							kX = i;
							kY = j; 
							kDist = k;
						}
				}
			if (kDist>0) break;
		}	
		worldMap[kY][kX] = keyNum;
	}
	while (keyNum>'s');

	// place some aliens behind corners
	
	for (k=4;k>0;k--)
	for (j=1;j<mapHeight;j+=2) 
		for (i=1;i<mapWidth;i+=2) 
		{
			if ((worldMap[j][i]==flr) && (((worldMap[j+1][i]==flr)+(worldMap[j-1][i]==flr)+(worldMap[j][i+1]==flr)+(worldMap[j][i-1]==flr)) == 3) ) 
			{
				if ((rand() & 3)==0) 
				{
					if	((cmap[j-1][i]>cmap[j][i] && worldMap[j-1][i]==flr) && (worldMap[j][i-1]==worldMap[j][i+1]))
						worldMap[j-(1+(rand()&1))][i] = 'a';
					else
					if	((cmap[j+1][i]>cmap[j][i] && worldMap[j+1][i]==flr) && (worldMap[j][i-1]==worldMap[j][i+1]))
						worldMap[j+(1+(rand()&1))][i] = 'a';
					else
					if	((cmap[j][i-1]>cmap[j][i] && worldMap[j][i-1]==flr) && (worldMap[j-1][i]==worldMap[j+1][i]))
						worldMap[j][i-(1+(rand()&1))] = 'a';
					else
					if	((cmap[j][i+1]>cmap[j][i] && worldMap[j][i+1]==flr) && (worldMap[j-1][i]==worldMap[j+1][i]))
						worldMap[j][i+(1+(rand()&1))] = 'a';
					
				}
			}
		}
	
	// create few random rooms removing some walls
	
	for (k=8;k>0;k--)
	for (j=0;j<mapHeight;j+=2) 
		for (i=0;i<mapWidth;i+=2) 
		{
			if ((rand() & 7) ==0)
				if ((worldMap[j][i]==wll) && (((worldMap[j+1][i]==flr)+(worldMap[j-1][i]==flr)+(worldMap[j][i+1]==flr)+(worldMap[j][i-1]==flr)) == 3) ) {
					worldMap[j][i] = flr;
				}
		}
		
	// on exit worldMap holds the actual map with flr,wll,doors,keys and alines
	// while cmap holds the path metrics
}

// actual maze generation

void	set(char x,char y,char v)
{
	if (v==15)
		worldMap[y][x]=wll;
	else
		worldMap[y][x]=v;
}


#define H (mapWidth-2)
#define V (mapHeight-2)

void rnd_maze(void)
{
	int	X,Y,C;
	char Lft,Rgth,Up,Dwn;

	// INITIALIZE FOUNDATION

	memset(worldMap,wll,mapWidth*mapHeight);
	
			 
	// SHOW ENTRANCE
	X=1+2*(rand()%(H/2));
	Y=1;
	StartX = X;
	StartY = Y;
	
	// FILL MAZE WITH PATHS UNTIL ALL CELLS HAVE BEEN CLEARED
	C=((H+1)/2)*((V+1)/2);
	while(1)
	{
L120:
		// CLEAR CURRENT CELL
		set(X,Y,0);
		C--;
		if (C<1) break;
		// JUST VISIT CURRENT CELL
		while (1)
		{
			// SURVEY SURROUNDING AREA
			Lft = (X>1) 	&& worldMap[Y][X-2];
			Rgth = (X<H-1) 	&& worldMap[Y][X+2];
			Up = (Y>1) 		&& worldMap[Y-2][X];
			Dwn = (Y<V-1) 	&& worldMap[Y+2][X];
			
			if (Lft||Rgth||Up||Dwn) 
				while (1)
				{
					// KNOCK DOWN A WALL IN A RANDOM BUT LEGAL DIRECTION
					switch (rand() & 3)
					{
						case 0:	if (Rgth) 	{set(X+1,Y,0);X+=2;goto	L120;}
						case 1:	if (Lft) 	{set(X-1,Y,0);X-=2;goto	L120;}
						case 2:	if (Up) 	{set(X,Y-1,0);Y-=2;goto	L120;}
						case 3:	if (Dwn) 	{set(X,Y+1,0);Y+=2;goto	L120;}
					}
				}			
			else
			{
				//IF DEAD END, JUMP TO A DIFFERENT LEGAL CELL
				do
				{
					Y+=2;
					if (Y>V) { Y=1;X+=2;if (X>H) X=1;}
				}
				while (worldMap[Y][X]); 
			}
		}
	}

	show(worldMap);
	popolate_level();
	
	// now worldMap holds the actual map with flr,wll,doors,keys and alines
	// while cmap holds the path metrics
	
	show(worldMap);
	//while(waitkey()!=0xFF) {}
	
	remap_level();
	
	show(worldMap);
	//while(waitkey()!=0xFF) {}
	
}

void tileload(void);

void fill_pattern(uchar i,uchar j,uchar n,char di,char dj) {
	uchar ii,jj;
	uint 	*p;

	p = &patterns[n*9];
	for	(jj=0;jj<3;jj++)
		for	(ii=0;ii<3;ii++){	
			h_map[j*3+jj+dj][i*3+ii+di] = *p++ ;
		}	

}

void remap_level(void) {
	uchar i,j;
	
	tileload();
	
	memcpy(cmap,worldMap,mapHeight*mapWidth);
	memcpy(map,worldMap,mapHeight*mapWidth);

	// clean worldMap by any attribute excep walls and floors
	for (j=0;j<mapHeight;j++) 
		for (i=0;i<mapWidth;i++)  {
			if (worldMap[j][i]!=wll) 
				worldMap[j][i]=flr;
				
			if ((cmap[j][i]<='Z')&&(cmap[j][i]>='S')) 
				worldMap[j][i]=wll;
			}
	
	for (i=0;i<mapWidth;i++) 
	{
		uchar *p = &worldMap[0][i]-mapWidth;
		*p = flr;						// safe due to a trick <==> worldMap[-1][i]=flr;
		worldMap[mapHeight-1][i] = flr;
	}
	for (j=0;j<mapHeight;j++) 
		worldMap[j][mapWidth-1]=flr;
	
	for (j=0;j<mapHeight;j++) 
		for (i=0;i<mapWidth;i++) 
		{
			if ((worldMap[j][i]==wll) && !((cmap[j][i]<='Z')&&(cmap[j][i]>='S'))){
			
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==wll))  {
					map[j][i] = 223 + 0;
					fill_pattern(i,j,0,0,0);
					}
				else
				 if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 1 ;
					fill_pattern(i,j,1,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 2;
					fill_pattern(i,j,2,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 3;
					fill_pattern(i,j,3,0,0);
					}
				else
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 4;
					fill_pattern(i,j,4,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 5;
					fill_pattern(i,j,5,0,0);
					}
				else
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 6;
					fill_pattern(i,j,6,0,0);
					}
				else
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 7;
					fill_pattern(i,j,7,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 8;
					fill_pattern(i,j,8,0,0);
					}
				else
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 9;
					fill_pattern(i,j,9,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 10;
					fill_pattern(i,j,10,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 11;
					fill_pattern(i,j,11,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 12;
					fill_pattern(i,j,12,0,0);
					}
				else
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 13;
					fill_pattern(i,j,13,0,0);
					}
				else
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==wll) && (worldMap[j][i+1]==wll) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 14;
					fill_pattern(i,j,14,0,0);
					}
				else
				if ((worldMap[j+1][i]==flr) && (worldMap[j-1][i]==flr) && (worldMap[j][i+1]==flr) && (worldMap[j][i-1]==flr)) {
					map[j][i] = 223 + 15;
					fill_pattern(i,j,15,0,0);
					}
			}
		}
	
	// manage doors, keys, aliens, slime and corposes
	for (j=1;j<mapHeight-1;j++) 
		for (i=1;i<mapWidth-1;i++) 
		{
			// doors
			if ((cmap[j][i]<='Z')&&(cmap[j][i]>='S')) {
				if ((worldMap[j][i+1]==wll) && (worldMap[j][i-1]==wll)) {
					map[j][i] = 223 + 16;
					fill_pattern(i,j,16,0,0);
					}
				else
				if ((worldMap[j+1][i]==wll) && (worldMap[j-1][i]==wll)) {
					map[j][i] = 223 + 17;
					fill_pattern(i,j,17,0,0);
					
					if	(map[j+1][i]==223 + 13)
						fill_pattern(i,j+1,18,0,0);
					else
					if	(map[j+1][i]==223 + 7)
						fill_pattern(i,j+1,19,0,0);
					else
					if	(map[j+1][i]==223 + 6)
						fill_pattern(i,j+1,20,0,0);
					else
					if	(map[j+1][i]==223 + 2)
						fill_pattern(i,j+1,21,0,0);
					else
					if	(map[j+1][i]==223 + 3)
						fill_pattern(i,j+1,22,0,0);
					}
			}
			else
			// keys
			if ((cmap[j][i]<='z')&&(cmap[j][i]>='s')){
				map[j][i] = 'k';
				fill_pattern(i,j,45,0,0);
			}
			else
			// aliens
			if ((cmap[j][i]=='a')) {
				map[j][i] = 'a';	
				if ((rand()&1)) {
					fill_pattern(i,j,28+(rand()&3),0,0);
					}
				else {
					fill_pattern(i,j,32+(rand()&3),0,0);
				}
			}
			else
			if (!(worldMap[j][i]==wll))	{
				// slime and corposes
				if ((rand()%50)==0)	{
					fill_pattern(i,j,40+(rand()%5),0,0);
					map[j][i] = '*';
				}
				else {
					// floor
					fill_pattern(i,j,27,0,0);
					map[j][i] = ' ';
				}
			}
		}

		// boxes
		for (j=1;j<mapHeight-1;j++) 
			for (i=1;i<mapWidth-1;i+=2) {
			
				if (((map[j][i]==223+5)||(map[j][i]==223+12)) && (map[j+1][i]==' ')) 	{
					char u = rand()&15;
					switch (u) {
						case 0: fill_pattern(i	,j,23,0,1);break;
						case 1: fill_pattern(i  ,j,24,0,1);break;
						case 2: fill_pattern(i	,j,25,0,2);break;
						case 3: fill_pattern(i	,j,26,0,2);break;
						default:
								break;
					}
				}
			}
			
		memcpy(worldMap,map,mapHeight*mapWidth);
}




#asm
global _tileload

_tileload:
	di
	ld	a,0xF8
	out (0x99),a
	ld	a,0x06+0x40
	out (0x99),a
	ei
	
	ld bc,256*18*8+0x98
	ld	hl,_shapes
	otir
	ret

_shapes:
db 00h,000h,000h,0F0h,010h,010h,010h,010h,000h,000h,000h,01Fh,010h,010h,010h,010h
db 10h,010h,010h,0F0h,000h,000h,000h,000h,010h,010h,010h,01Fh,000h,000h,000h,000h
db 00h,000h,000h,0FFh,010h,010h,010h,010h,010h,010h,010h,0FFh,000h,000h,000h,000h
db 10h,010h,010h,0F0h,010h,010h,010h,010h,010h,010h,010h,01Fh,010h,010h,010h,010h
db 10h,010h,010h,010h,000h,000h,000h,000h,000h,000h,000h,000h,010h,010h,010h,010h
db 00h,000h,000h,00Fh,000h,000h,000h,000h,000h,000h,000h,0F0h,000h,000h,000h,000h
db 00h,000h,000h,0FFh,000h,000h,000h,000h,010h,010h,010h,010h,010h,010h,010h,010h
db 10h,010h,010h,0FFh,010h,010h,010h,010h,000h,000h,010h,000h,000h,000h,000h,000h

db 00h,000h,0AAh,0FFh,055h,000h,000h,000h,018h,030h,018h,030h,018h,030h,018h,030h

global _patterns
_patterns:
*include asciitst\patterns.asm
#endasm



#asm
	psect	bss,class=DATA

global _hidden_map,_h_map

_hidden_map:
_h_map:
 defs	2*128*MAX_Y
 
#endasm








